package com.huishu.gateway.filter;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.huishu.commons.exception.RequestAuthenticationException;
import com.huishu.commons.pojo.Result;
import com.huishu.commons.utlis.JwtUtil;
import com.huishu.gateway.properties.IgnoreTokenProperties;
import com.huishu.gateway.utils.AesEncrypt;
import com.huishu.commons.utlis.MD5Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Date;
import java.util.function.Consumer;

@Component
@Slf4j
public class AuthorizeFilter implements GlobalFilter, Ordered {

    private static final String X_CURTIME = "X-CurTime";
    private static final String X_SIGN = "X-Sign";
    private static final String X_PARAMS = "X-Params";
    private static final int TIME_ERROR_RANGE = 5 * 60 * 1000 + 100 * 60 * 1000;

    @Autowired
    private IgnoreTokenProperties ignoreTokenProperties;

    /**
     * 忽略应用级token
     *
     * @return
     */
    private boolean isIgnoreToken(String path) {
        return ignoreTokenProperties.isIgnoreToken(path);
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put("requestTime", System.currentTimeMillis());
        ServerHttpRequest request = exchange.getRequest();
        // 获取请求uri 检查白名单（配置）
        String uri = request.getURI().getPath();
        // 获取请求token
        String token = request.getHeaders().getFirst("Authorization");
        log.info("请求 TOKEN：{} ", token);
        exchange.getAttributes().put("requestUri", uri);
        log.info("请求 URI：{} ", uri);
        if (uri.contains("/order/order/notify")) {
            log.info("支付回调....");
            return chain.filter(exchange.mutate().request(request).build());
        }
        verificationHeader(request);
        if (isIgnoreToken(uri)) {
            if (StringUtils.isEmpty(token)) {
                log.info("URI通过白名单不校验TOKEN isIgnoreToken");
                return chain.filter(exchange.mutate().request(request).build());
            }
        }

        if (StringUtils.isBlank(token)) {
            return Mono.error(new ResponseStatusException(HttpStatus.UNAUTHORIZED, "token is not null"));
        }
        DecodedJWT verify = JwtUtil.verify(token);
        if (verify == null) {
            return Mono.error(new ResponseStatusException(HttpStatus.UNAUTHORIZED, "token verify error"));
        }
        String payloadEncStr = verify.getPayload();
        String payload = new String(Base64.decodeBase64(payloadEncStr));
        log.info(payload);
        Long userId = verify.getClaim("uid").asLong();
        Long adminUid = verify.getClaim("adminUid").asLong();
        // 将常用的参数存到请求头
        Consumer<HttpHeaders> httpHeaders = httpHeader -> {
            httpHeader.set("uid", userId == null ? "" : String.valueOf(userId));
            httpHeader.set("adminUid", adminUid == null ? "" : String.valueOf(adminUid));
        };
        ServerHttpRequest mutableReq = request.mutate().headers(httpHeaders).build();
        ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
        return chain.filter(mutableExchange);
    }

    private void verificationHeader(ServerHttpRequest request) {
        String sign = request.getHeaders().getFirst(X_SIGN);
        if (StringUtils.isBlank(sign)) {
            throw new RequestAuthenticationException(X_SIGN + " is blank");
        }
        String curTime = request.getHeaders().getFirst(X_CURTIME);
        if (StringUtils.isBlank(curTime)) {
            throw new RequestAuthenticationException(X_CURTIME + " is blank");
        }
        String params = request.getHeaders().getFirst(X_PARAMS);
        log.info("X-params -> {}", params);
        /* 5分钟时间误差 */
        long nowTime = new Date().getTime();
        long requestTime = Long.parseLong(curTime);
        if (requestTime > nowTime + TIME_ERROR_RANGE
                || requestTime < nowTime - TIME_ERROR_RANGE) {
            log.warn("X-CurTime -> {}, nowTime -> {}", curTime, nowTime);
            throw new RequestAuthenticationException("X-CurTime time expire");
        }

        String str = AesEncrypt.getKey() + curTime;
        if (StringUtils.isNotBlank(params)) {
            str += params;
        }
        if (!sign.equals(MD5Utils.hexMd5(str))) {
            throw new RequestAuthenticationException("Signature verification failed");
        }
    }

    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
        String result = Result.custom(String.valueOf(HttpStatus.UNAUTHORIZED.value()), HttpStatus.UNAUTHORIZED.getReasonPhrase()).toString();
        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(result.getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }

    /**
     * 值越大执行优先级越低
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 1;
    }
}
